﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using ELearning.Common.Extensions;

namespace ELearning.Common.Helpers
{
    public class TencentCloudHelper
    {
        private static string ReqUrlFormat = "https://vod.tencentcloudapi.com/";
        /// <summary>
        /// SecretId
        /// </summary>
        private const string SecretId = "AKIDg8Mr7YzespWRO6Qtv3vbCVAa8XiErR5V";

        /// <summary>
        /// SecretKey
        /// </summary>
        private const string SecretKey = "JQJtKik0ED7mCOwFJBpgcKp57tOvZzFR";

        /// <summary>
        /// APPID
        /// </summary>
        private const string APPID = "1302181331";

        private static string Region { set; get; }//区域
        private static long Timestamp { set; get; }//当前时间戳
        private static int Nonce { set; get; }//随机正整数:
        private static string Signature { set; get; }//签名
        private static string Version { set; get; } = "2018-07-17";
        private static string Action { set; get; } = "";
        private static string MediaType { set; get; } = "mp4";
        private static string ClassId { set; get; } = "679959";

        public static string EnSignature { set; get; }

        private static string ReqUrlForVideo { set; get; } = "https://vod.api.qcloud.com/v2/index.php";

        /**
         *
         * https://cloud.tencent.com/document/product/266/8586
         * 获取视频详细信息
         */

        /// <summary>
        /// 视频转码参数拼接
        /// </summary>
        /// <param name="fileId">文件id</param>
        public static string TranscodingForCut(string fileId)
        {
            Region = "gz";
            Nonce = new Random().Next(0, 99999999);

            //签名原文的拼接规则为:
            //请求方法 + 请求主机 + 请求路径 + ? +请求字符串
            var ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            Timestamp = Convert.ToInt64(ts.TotalSeconds);//获取时间戳

            var str = $"GETvod.tencentcloudapi.com/?Action=ProcessMedia&FileId={fileId}&MediaProcessTask.TranscodeTaskSet.0.Definition=100230&Nonce={Nonce}&SecretId={SecretId}&Timestamp={Timestamp}&Version=2018-07-17";

            Signature = QuerySignature(str);

            var para = $"Action=ProcessMedia&FileId={fileId}&MediaProcessTask.TranscodeTaskSet.0.Definition=100230&Nonce={Nonce}&SecretId={SecretId}&Signature={Signature}&Timestamp={Timestamp}&Version=2018-07-17";

            return $"{ReqUrlFormat}?{para}";
        }

        /// <summary>
        /// 开始转码
        /// </summary>
        /// <param name="fileId">文件id</param>
        public static string StartConvertVideo(string fileId)
        {
            var transcodingUrl = TranscodingForCut(fileId);
            var startResult = SendMessage(transcodingUrl);
            return startResult;
        }

        /// <summary>
        /// 只是发送get请求,带有返回值
        /// </summary>
        public static string SendMessage(string url, Encoding encode = null)
        {
            string result;
            try
            {
                var webClient = new WebClient { Encoding = Encoding.UTF8 };
                if (encode != null)
                    webClient.Encoding = encode;
                result = webClient.DownloadString(url);
            }
            catch (Exception ex)
            {
                result = ex.Message;
            }
            return result;
        }

        /// <summary>
        /// 生成签名 
        /// 要穿的参数都要加密
        /// </summary>
        private static string QuerySignature(string str, string key = SecretKey)
        {
            var hmacsha1 = new HMACSHA1(Encoding.UTF8.GetBytes(key));
            var rstRes = hmacsha1.ComputeHash(Encoding.UTF8.GetBytes(str));
            var strs = Convert.ToBase64String(rstRes);
            return System.Web.HttpUtility.UrlEncode(strs);
        }

        /// <summary>
        /// 获取时间戳
        /// </summary>
        /// <returns></returns>
        private static void SetTimestamp()
        {
            var ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            Timestamp = Convert.ToInt64(ts.TotalSeconds);//获取时间戳
        }

        /// <summary>
        /// 申请上传
        /// </summary>
        public static string ApplyUpload(string path)
        {
            Action = "ApplyUpload";
            Region = "ap-chongqing";
            SetTimestamp();
            var str = $"GETvod.tencentcloudapi.com/?Action={Action}&ClassId={ClassId}&MediaType={MediaType}&Nonce={Nonce}&Region={Region}&SecretId={SecretId}&Timestamp={Timestamp}&Version={Version}";
            //var str = $"GETcvm.tencentcloudapi.com/?Action=DescribeInstances&InstanceIds.0=ins-09dx96dg&Limit=20&Nonce=11886&Offset=0&Region=ap-guangzhou&SecretId=AKIDz8krbsJ5yKBZQpn74WFkmLPx3EXAMPLE&Timestamp=1465185768&Version=2017-03-12";
            Signature = QuerySignature(str);
            var para = $"Action={Action}&ClassId={ClassId}&MediaType={MediaType}&Nonce={Nonce}&Region={Region}&SecretId={SecretId}&Signature={Signature}&Timestamp={Timestamp}&Version={Version}";
            var res = ReqUrl($"{ReqUrlFormat}?{para}");
            var m = res.ToObject<ApplyUploadModel>();

            PutVideo(path, m);
            return res;
        }

        private static string ReqUrl(string reqUrl)
        {
            try
            {
                var request = WebRequest.Create(reqUrl) as HttpWebRequest;
                using (var resp = request.GetResponse() as HttpWebResponse)
                {
                    //System.Threading.Thread.Sleep(2000);
                    using (var stream = new StreamReader(resp.GetResponseStream(), Encoding.UTF8))
                    {
                        //System.Threading.Thread.Sleep(1000);
                        var result = stream.ReadToEnd();
                        //System.Threading.Thread.Sleep(1000);
                        return result;
                    }
                }
            }
            catch (Exception ex) { return ex.ToString(); }
        }

        #region 上传文件签名使用

        private static string GetPutAuthorization(int contentLength, string dt, string host, ApplyUploadModel vm)
        {
            var startTimestamp = GetTimestamp();
            var passDate = DateTime.Now.AddMinutes(30);
            var endTimestamp = GetTimestamp(passDate);
            var keyTime = $"{startTimestamp};{endTimestamp}";

            //SignKey
            var signKey = QuerySignature(keyTime);

            var headerList = "content-length;content-type;date;host;";
            var httpHeaders = $"content-length={contentLength};content-type={HttpUtility.UrlEncode("application/octet-stream")};date={HttpUtility.UrlEncode(dt)};host={HttpUtility.UrlEncode(host)};";
            var httpString = $@"PUT\n{ vm.Response.MediaStoragePath}\n\n{httpHeaders}";

            //SHA1加密方法
            var sha1 = new SHA1CryptoServiceProvider();
            byte[] str01 = Encoding.Default.GetBytes(httpString);
            byte[] str02 = sha1.ComputeHash(str01);

            var strs = Convert.ToBase64String(str02);
            var t1 = System.Web.HttpUtility.UrlEncode(strs);

            var stringToSign = $@"sha1\n{keyTime}\n{t1}\n";

            var signature = QuerySignature(stringToSign, signKey);

            var authorization = $"q-sign-algorithm=sha1&q-ak={SecretId}&q-sign-time={keyTime}&q-key-time={keyTime}&q-header-list={headerList}&q-url-param-list=&q-signature={signature}";

            return authorization;
        }

        /// <summary>
        /// MD5字符串加密
        /// </summary>
        /// <param name="txt"></param>
        /// <returns>加密后字符串</returns>
        public static string GenerateMD5(string txt)
        {
            using (MD5 mi = MD5.Create())
            {
                byte[] buffer = Encoding.Default.GetBytes(txt);
                //开始加密
                byte[] newBuffer = mi.ComputeHash(buffer);
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < newBuffer.Length; i++)
                {
                    sb.Append(newBuffer[i].ToString("x2"));
                }
                return sb.ToString();
            }
        }

        /// <summary>
        /// 获取时间戳，如果不传则获取当前时间戳
        /// </summary>
        /// <param name="dt">时间</param>
        private static long GetTimestamp(DateTime? dt = null)
        {
            var ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            var timestamp = Convert.ToInt64(ts.TotalSeconds);//获取时间戳
            if (dt.IsNotNull())
            {
                ts = dt.Value - new DateTime(1970, 1, 1, 0, 0, 0, 0);
                timestamp = Convert.ToInt64(ts.TotalSeconds);//获取时间戳
            }

            return timestamp;
        }

        #endregion

        /// <summary>
        /// 上传视频
        /// </summary>
        private static void PutVideo(string path, ApplyUploadModel vm)
        {
            FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);

            byte[] bArr = new byte[fs.Length];

            fs.Read(bArr, 0, bArr.Length);

            fs.Close();

            var host = $"{vm.Response.StorageBucket}-{APPID}.cos.{Region}.myqcloud.com";
            var date = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            var authorization = GetPutAuthorization(bArr.Length, date, host, vm);
            var url = vm.Response.MediaStoragePath;
            var headers =
                new Dictionary<string, string>
                {
                    { "Authorization", authorization },
                    { "Content-Length", bArr.Length.ToString()},
                    { "Date", date },
                    { "Host",  host }
                };
            var client = (HttpWebRequest)WebRequest.Create(url);
            foreach (var header in headers)
            {
                client.Headers.Add(header.Key, header.Value);
            }
            client.Method = "PUT";
            client.ContentType = "application/octet-stream";


            client.ContentLength = bArr.Length;
            client.GetRequestStream().Write(bArr, 0, bArr.Length);
            var response = (HttpWebResponse)client.GetResponse();
            var b = response.StatusCode == HttpStatusCode.OK;
        }

        /// <summary>
        /// 上传视频
        /// </summary>
        private static void PutVideo(string reqUrl, string path)
        {
            var request = WebRequest.Create(reqUrl) as HttpWebRequest;

            CookieContainer cookieContainer = new CookieContainer();

            request.CookieContainer = cookieContainer;

            request.AllowAutoRedirect = true;

            request.Method = "PUT";

            string boundary = DateTime.Now.Ticks.ToString("X"); // 随机分隔线

            request.ContentType = "multipart/form-data;charset=utf-8;boundary=" + boundary;

            byte[] itemBoundaryBytes = Encoding.UTF8.GetBytes("\r\n--" + boundary + "\r\n");

            byte[] endBoundaryBytes = Encoding.UTF8.GetBytes("\r\n--" + boundary + "--\r\n");
            int pos = path.LastIndexOf("\\");

            string fileName = path.Substring(pos + 1);
            //请求头部信息 

            StringBuilder sbHeader = new StringBuilder(string.Format("Content-Disposition:form-data;name=\"file\";filename=\"{0}\"\r\nContent-Type:application/octet-stream\r\n\r\n", fileName));

            byte[] postHeaderBytes = Encoding.UTF8.GetBytes(sbHeader.ToString());
            FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);

            byte[] bArr = new byte[fs.Length];

            fs.Read(bArr, 0, bArr.Length);

            fs.Close();
            Stream postStream = request.GetRequestStream();

            postStream.Write(itemBoundaryBytes, 0, itemBoundaryBytes.Length);

            postStream.Write(postHeaderBytes, 0, postHeaderBytes.Length);

            postStream.Write(bArr, 0, bArr.Length);

            postStream.Write(endBoundaryBytes, 0, endBoundaryBytes.Length);

            postStream.Close();
            //发送请求并获取相应回应数据

            HttpWebResponse response = request.GetResponse() as HttpWebResponse;

            //直到request.GetResponse()程序才开始向目标网页发送Post请求

            Stream instream = response.GetResponseStream();

            StreamReader sr = new StreamReader(instream, Encoding.UTF8);

            //返回结果网页（html）代码

            string content = sr.ReadToEnd();
        }
    }

    #region 腾讯云获取签名相关demo和类

    //腾讯云官方提供的demo
    public class Signature
    {
        /// <summary>
        /// 构造签名明文串的参数，云 API 密钥中的 Secret ID。
        /// </summary>
        public string MStrSecId = "AKIDg8Mr7YzespWRO6Qtv3vbCVAa8XiErR5V";

        public string MStrSecKey = "JQJtKik0ED7mCOwFJBpgcKp57tOvZzFR";

        /// <summary>
        /// 构造签名明文串的参数，无符号32位随机数。
        /// </summary>
        public int MiRandom;

        /// <summary>
        /// 构造签名明文串的参数，当前 Unix 时间。
        /// </summary>
        public long MQwNowTime;

        /// <summary>
        /// 签名有效时长签名有效时长最大取值为7776000，即90天
        /// </summary>
        public int MiSignValidDuration;

        /// <summary>
        /// 获取时间戳
        /// </summary>
        public static long GetIntTimeStamp()
        {
            var ts = DateTime.UtcNow - new DateTime(1970, 1, 1);
            return Convert.ToInt64(ts.TotalSeconds);
        }

        /// <summary>
        /// 根据秘钥把签名加密成哈希值
        /// </summary>
        /// <param name="signatureString">签名串</param>
        /// <param name="secretKey">秘钥</param>
        /// <returns></returns>
        private static byte[] hash_hmac_byte(string signatureString, string secretKey)
        {
            var enc = Encoding.UTF8;
            var hmac = new HMACSHA1(enc.GetBytes(secretKey));
            hmac.Initialize();
            var buffer = enc.GetBytes(signatureString);
            return hmac.ComputeHash(buffer);
        }

        /// <summary>
        /// 获取上传签名
        /// </summary>
        public string GetUploadSignature()
        {
            var strContent = $"secretId={Uri.EscapeDataString(MStrSecId)}&currentTimeStamp={MQwNowTime}&expireTime={MQwNowTime}{MiSignValidDuration}&random={MiRandom}";

            var bytesSign = hash_hmac_byte(strContent, MStrSecKey);
            var byteContent = Encoding.Default.GetBytes(strContent);
            var nCon = new byte[bytesSign.Length + byteContent.Length];
            bytesSign.CopyTo(nCon, 0);
            byteContent.CopyTo(nCon, bytesSign.Length);
            return Convert.ToBase64String(nCon);
        }
    }

    #endregion

    public class ApplyUploadModel
    {
        public ApplyUploadResponseModel Response { set; get; } = new ApplyUploadResponseModel();
    }

    public class ApplyUploadResponseModel
    {
        /// <summary>
        /// 封面存储路径，用于上传接口存储封面的对象键（Key）
        /// </summary>
        public string CoverStoragePath { set; get; }

        /// <summary>
        /// 唯一请求 ID，每次请求都会返回。定位问题时需要提供该次请求的 RequestId。
        /// </summary>
        public string RequestId { set; get; }

        /// <summary>
        /// 存储桶，用于上传接口 URL 的 bucket_name。
        /// </summary>
        public string StorageBucket { set; get; }

        /// <summary>
        /// 存储园区，用于上传接口 Host 的 Region。
        /// </summary>
        public string StorageRegion { set; get; }

        /// <summary>
        /// 媒体存储路径，用于上传接口存储媒体的对象键（Key）。
        /// </summary>
        public string MediaStoragePath { set; get; }

        /// <summary>
        /// 点播会话，用于确认上传接口的参数 VodSessionKey。
        /// </summary>
        public string VodSessionKey { set; get; }
        public ApplyUploadTempCertificateModel TempCertificate { set; get; } = new ApplyUploadTempCertificateModel();
    }

    public class ApplyUploadTempCertificateModel
    {
        public string SecretId { set; get; }
        public string SecretKey { set; get; }
        public string Token { set; get; }
        public long ExpiredTime { set; get; }
    }
}
